#if VS_TESSELLATION

#include "CommonTessellation.cfi"

/////////////////////////////////////////////////
// GENERAL PASS                                //
/////////////////////////////////////////////////

#define vWorldViewPos g_VS_WorldViewPos.xyz

///////////////// Hull shaders //////////////////

HS_CONSTANT_DATA_OUTPUT SkinConstantsHS(InputPatch<vert2FragGeneral, 3> p, uint PatchID : SV_PrimitiveID)
{
	const float3 vP0 = p[0].vView.xyz + vWorldViewPos;
	const float3 vP1 = p[1].vView.xyz + vWorldViewPos;
	const float3 vP2 = p[2].vView.xyz + vWorldViewPos;
	HS_CONSTANT_DATA_OUTPUT output = CommonConstantsHS(vP0, vP1, vP2, vWorldViewPos, vWorldViewPos, PatchID);

	return output;
}

[domain("tri")]
[partitioning("fractional_odd")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(3)]
[patchconstantfunc("SkinConstantsHS")]
[maxtessfactor(MAX_TESS_FACTOR)]
HS_CONTROL_POINT_OUTPUT_ILLUM SkinHS(InputPatch<vert2FragGeneral, 3> inputPatch, uint uCPID : SV_OutputControlPointID)
{
	HS_CONTROL_POINT_OUTPUT_ILLUM output = (HS_CONTROL_POINT_OUTPUT_ILLUM)0;

#if %_RT_FOG ||%_RT_DECAL_TEXGEN_2D
	output.Ambient                 = inputPatch[uCPID].Ambient;
#endif
#if %_RT_FOG || %MULTILAYER
	output.vTangent                = inputPatch[uCPID].vTangent;
	output.vBinormal               = inputPatch[uCPID].vBinormal;
#endif
	output.controlPoint.vView      = inputPatch[uCPID].vView;
	output.controlPoint.vNormal    = inputPatch[uCPID].vNormal.xyz;
	output.controlPoint.vBaseTC    = inputPatch[uCPID].baseTC;
	output.screenProj              = inputPatch[uCPID].screenProj;
#if %_LT_LIGHTS && %_LT_HASPROJ
	output.projTC                  = inputPatch[uCPID].projTC;
#endif
#if %_RT_DECAL_TEXGEN_2D
	output.DistAtten               = inputPatch[uCPID].DistAtten;
#endif
#if %_RT_FOG && !%_RT_DECAL_TEXGEN_2D
	output.AvgFogVolumeContrib     = inputPatch[uCPID].AvgFogVolumeContrib;
#endif

	return output;
}

///////////////// Domain shaders //////////////////

void FillEvalInput(in HS_CONTROL_POINT_OUTPUT_ILLUM triPatch[3], in HS_CONSTANT_DATA_OUTPUT hsConstData, in float3 vBaryCoords, inout CommonEvaluationInputDS evalInput)
{
	evalInput.vBaryCoords = vBaryCoords;
	FillEvalInputControlPoint(triPatch[0].controlPoint, triPatch[1].controlPoint, triPatch[2].controlPoint, hsConstData, evalInput);
	evalInput.bRelativePos = true;
#if %_RT_FOG || %MULTILAYER
	FillEvalInputTangentBinormal(triPatch[0].vTangent, triPatch[1].vTangent, triPatch[2].vTangent, triPatch[0].vBinormal, triPatch[1].vBinormal, triPatch[2].vBinormal, evalInput);
#endif
	evalInput.vViewPos = vWorldViewPos;
}

void ProcessEvalOutput(in CommonEvaluationOutputDS evalOutput, inout vert2FragGeneral dsOutput)
{
#if %_RT_FOG || %MULTILAYER
	dsOutput.vTangent = evalOutput.vTangent;
	dsOutput.vBinormal = evalOutput.vBinormal;
#endif
	dsOutput.baseTC = evalOutput.vBaseTC;

	dsOutput.HPosition = mul(g_VS_ViewProjZeroMatr, float4(evalOutput.vPos.xyz, 1));
}

[domain("tri")]
vert2FragGeneral SkinDS( HS_CONSTANT_DATA_OUTPUT hsConstData, float3 vBaryCoords : SV_DomainLocation, const OutputPatch<HS_CONTROL_POINT_OUTPUT_ILLUM, 3> TrianglePatch )
{
	vert2FragGeneral output = (vert2FragGeneral)0;
#if %_RT_FOG ||%_RT_DECAL_TEXGEN_2D
	output.Ambient = EvalVec(TrianglePatch[0].Ambient, TrianglePatch[1].Ambient, TrianglePatch[2].Ambient, vBaryCoords);
#endif

	CommonEvaluationInputDS evalInput = (CommonEvaluationInputDS)0;
	FillEvalInput(TrianglePatch, hsConstData, vBaryCoords, evalInput);

	CommonEvaluationOutputDS evalOutput;
	Evaluate(evalInput, evalOutput);
	ProcessEvalOutput(evalOutput, output);

#if %_RT_FOG && !%_RT_DECAL_TEXGEN_2D
	output.AvgFogVolumeContrib = EvalVec(TrianglePatch[0].AvgFogVolumeContrib, TrianglePatch[1].AvgFogVolumeContrib, TrianglePatch[2].AvgFogVolumeContrib, vBaryCoords);
#endif
	float4 vScreenProj = HPosToScreenTC(output.HPosition);
#if %WRINKLE_BLENDING
	#if _RT_SKELETON_SSD && _RT_MORPHTARGET
		vScreenProj.z = EvalVec(TrianglePatch[0].screenProj, TrianglePatch[1].screenProj, TrianglePatch[2].screenProj, vBaryCoords).z;
	#endif
#endif
	output.screenProj = vScreenProj;

	return output;
}

/////////////////////////////////////////////////
// Z PASS                                      //
/////////////////////////////////////////////////

///////////////// Hull shaders //////////////////

HS_CONSTANT_DATA_OUTPUT SkinConstantsZPassHS(InputPatch<vert2fragZ_C, 3> p, uint PatchID : SV_PrimitiveID)
{
	const float3 vP0 = p[0].vView.xyz + vWorldViewPos;
	const float3 vP1 = p[1].vView.xyz + vWorldViewPos;
	const float3 vP2 = p[2].vView.xyz + vWorldViewPos;
	HS_CONSTANT_DATA_OUTPUT output = CommonConstantsHS(vP0, vP1, vP2, vWorldViewPos, vWorldViewPos, PatchID);

	return output;
}

[domain("tri")]
[partitioning("fractional_odd")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(3)]
[patchconstantfunc("SkinConstantsZPassHS")]
[maxtessfactor(64.0)]
HS_CONTROL_POINT_OUTPUT_ZPASS SkinZPassHS(InputPatch<vert2fragZ_C, 3> inputPatch, uint uCPID : SV_OutputControlPointID)
{
	HS_CONTROL_POINT_OUTPUT_ZPASS	output = (HS_CONTROL_POINT_OUTPUT_ZPASS)0;

	output.controlPoint.vView     = inputPatch[uCPID].vView;
	output.controlPoint.vNormal   = inputPatch[uCPID].vNormal.xyz;
	output.controlPoint.vBaseTC   = inputPatch[uCPID].baseTC;
	output.vTangent               = inputPatch[uCPID].vTangent;
	output.vBinormal              = inputPatch[uCPID].vBinormal;
	output.vZInfo                 = inputPatch[uCPID].ZInfo;

	return output;
}

///////////////// Domain shaders //////////////////

void FillEvalInput(in HS_CONTROL_POINT_OUTPUT_ZPASS triPatch[3], in HS_CONSTANT_DATA_OUTPUT hsConstData, in float3 vBaryCoords, inout CommonEvaluationInputDS evalInput)
{
	evalInput.vBaryCoords = vBaryCoords;
	FillEvalInputControlPoint(triPatch[0].controlPoint, triPatch[1].controlPoint, triPatch[2].controlPoint, hsConstData, evalInput);
	evalInput.bRelativePos = true;
	FillEvalInputTangentBinormal(triPatch[0].vTangent, triPatch[1].vTangent, triPatch[2].vTangent, triPatch[0].vBinormal.xyzz, triPatch[1].vBinormal.xyzz, triPatch[2].vBinormal.xyzz, evalInput);
	evalInput.vViewPos = vWorldViewPos;
}

void ProcessEvalOutput(in CommonEvaluationOutputDS evalOutput, inout vert2fragZ_C dsOutput)
{
	dsOutput.baseTC    = evalOutput.vBaseTC;
	dsOutput.vTangent  = evalOutput.vTangent;
	dsOutput.vBinormal = evalOutput.vBinormal.xyz;
	dsOutput.vNormal.xyz = evalOutput.vNormal;

	dsOutput.HPosition = mul(g_VS_ViewProjZeroMatr, float4(evalOutput.vPos.xyz, 1));
}

[domain("tri")]
vert2fragZ_C SkinZPassDS( HS_CONSTANT_DATA_OUTPUT hsConstData, float3 vBaryCoords : SV_DomainLocation, const OutputPatch<HS_CONTROL_POINT_OUTPUT_ZPASS, 3> TrianglePatch )
{
	vert2fragZ_C output = (vert2fragZ_C)0;

	CommonEvaluationInputDS evalInput = (CommonEvaluationInputDS)0;
	FillEvalInput(TrianglePatch, hsConstData, vBaryCoords, evalInput);

	CommonEvaluationOutputDS evalOutput;
	Evaluate(evalInput, evalOutput);
	ProcessEvalOutput(evalOutput, output);

	// Output depth
	output.ZInfo = EvalVec(TrianglePatch[0].vZInfo, TrianglePatch[1].vZInfo, TrianglePatch[2].vZInfo, vBaryCoords);
	output.ZInfo.x = output.HPosition.w * g_VS_NearFarClipDist.w;
#if %_RT_NEAREST
	output.vZInfo.x *= g_VS_NearFarClipDist.z;
#endif

	return output;
}

#endif